0a06bc
@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2017 the original author or authors.
+ * Copyright 2002-2018 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -47,6 +47,7 @@
import org.springframework.util.ReflectionUtils;
  * (right now), so the names must be unique.
  *
  * @author Andy Clement
+ * @author Juergen Hoeller
  * @since 3.0
  */
 public class FunctionReference extends SpelNodeImpl {
@@ -56,9 +57,7 @@
public class FunctionReference extends SpelNodeImpl {
 	// Captures the most recently used method for the function invocation *if* the method
 	// can safely be used for compilation (i.e. no argument conversion is going on)
 	@Nullable
-	private Method method;
-	
-	private boolean argumentConversionOccurred;
+	private volatile Method method;
 
 
 	public FunctionReference(String functionName, int pos, SpelNodeImpl... arguments) {
@@ -90,14 +89,13 @@
public class FunctionReference extends SpelNodeImpl {
 	}
 
 	/**
-	 * Execute a function represented as a java.lang.reflect.Method.
+	 * Execute a function represented as a {@code java.lang.reflect.Method}.
 	 * @param state the expression evaluation state
 	 * @param method the method to invoke
 	 * @return the return value of the invoked Java method
 	 * @throws EvaluationException if there is any problem invoking the method
 	 */
 	private TypedValue executeFunctionJLRMethod(ExpressionState state, Method method) throws EvaluationException {
-		this.method = null;
 		Object[] functionArgs = getArguments(state);
 
 		if (!method.isVarArgs() && method.getParameterCount() != functionArgs.length) {
@@ -112,25 +110,33 @@
public class FunctionReference extends SpelNodeImpl {
 
 		// Convert arguments if necessary and remap them for varargs if required
 		TypeConverter converter = state.getEvaluationContext().getTypeConverter();
-		argumentConversionOccurred = ReflectionHelper.convertAllArguments(converter, functionArgs, method);
+		boolean argumentConversionOccurred = ReflectionHelper.convertAllArguments(converter, functionArgs, method);
 		if (method.isVarArgs()) {
 			functionArgs = ReflectionHelper.setupArgumentsForVarargsInvocation(
 					method.getParameterTypes(), functionArgs);
 		}
+		boolean compilable = false;
 
 		try {
 			ReflectionUtils.makeAccessible(method);
 			Object result = method.invoke(method.getClass(), functionArgs);
-			if (!argumentConversionOccurred) {
-				this.method = method;
-				this.exitTypeDescriptor = CodeFlow.toDescriptor(method.getReturnType());
-			}
+			compilable = !argumentConversionOccurred;
 			return new TypedValue(result, new TypeDescriptor(new MethodParameter(method, -1)).narrow(result));
 		}
 		catch (Exception ex) {
 			throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.EXCEPTION_DURING_FUNCTION_CALL,
 					this.name, ex.getMessage());
 		}
+		finally {
+			if (compilable) {
+				this.exitTypeDescriptor = CodeFlow.toDescriptor(method.getReturnType());
+				this.method = method;
+			}
+			else {
+				this.exitTypeDescriptor = null;
+				this.method = null;
+			}
+		}
 	}
 
 	@Override
@@ -162,12 +168,13 @@
public class FunctionReference extends SpelNodeImpl {
 	
 	@Override
 	public boolean isCompilable() {
-		if (this.method == null || this.argumentConversionOccurred) {
+		Method method = this.method;
+		if (method == null) {
 			return false;
 		}
-		int methodModifiers = this.method.getModifiers();
+		int methodModifiers = method.getModifiers();
 		if (!Modifier.isStatic(methodModifiers) || !Modifier.isPublic(methodModifiers) ||
-				!Modifier.isPublic(this.method.getDeclaringClass().getModifiers())) {
+				!Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
 			return false;
 		}
 		for (SpelNodeImpl child : this.children) {
@@ -179,12 +186,13 @@
public class FunctionReference extends SpelNodeImpl {
 	}
 	
 	@Override 
-	public void generateCode(MethodVisitor mv,CodeFlow cf) {
-		Assert.state(this.method != null, "No method handle");
-		String classDesc = this.method.getDeclaringClass().getName().replace('.', '/');
-		generateCodeForArguments(mv, cf, this.method, this.children);
-		mv.visitMethodInsn(INVOKESTATIC, classDesc, this.method.getName(),
-				CodeFlow.createSignatureDescriptor(this.method), false);
+	public void generateCode(MethodVisitor mv, CodeFlow cf) {
+		Method method = this.method;
+		Assert.state(method != null, "No method handle");
+		String classDesc = method.getDeclaringClass().getName().replace('.', '/');
+		generateCodeForArguments(mv, cf, method, this.children);
+		mv.visitMethodInsn(INVOKESTATIC, classDesc, method.getName(),
+				CodeFlow.createSignatureDescriptor(method), false);
 		cf.pushDescriptor(this.exitTypeDescriptor);
 	}
 
